package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.UserServiceSubscriptionDTO;
import info.batcloud.fanli.core.constants.CacheNameConstants;
import info.batcloud.fanli.core.entity.UserServiceSubscription;
import info.batcloud.fanli.core.enums.Service;
import info.batcloud.fanli.core.enums.UserServiceSubscriptionStatus;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.repository.UserServiceSubscriptionRepository;
import info.batcloud.fanli.core.service.UserServiceSubscriptionService;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.Date;
import java.util.List;

@org.springframework.stereotype.Service
@CacheConfig(cacheNames = CacheNameConstants.USER_SERVICE_SUBSCRIPTION)
public class UserServiceSubscriptionServiceImpl implements UserServiceSubscriptionService {

    @Inject
    private UserServiceSubscriptionRepository userServiceSubscriptionRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private UserServiceSubscriptionService userServiceSubscriptionService;

    @Override
    public Paging<UserServiceSubscriptionDTO> search(SearchParam param) {
        Specification<UserServiceSubscription> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (StringUtils.isNotBlank(param.getPhone())) {
                expressions.add(cb.equal(root.get("user").get("phone"), param.getPhone()));
            }
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("status"), param.getStatus()));
            } else {
                expressions.add(cb.notEqual(root.get("status"), UserServiceSubscriptionStatus.DELETED));
            }
            if (param.getUserId() != null) {
                expressions.add(cb.equal(root.get("user").get("id"), param.getUserId()));
            }
            if (param.getService() != null) {
                expressions.add(cb.equal(root.get("service"), param.getService()));
            }
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<UserServiceSubscription> page = userServiceSubscriptionRepository.findAll(specification, pageable);
        return PagingHelper.of(page, item -> toBO(item), param.getPage(), param.getPageSize());
    }

    private UserServiceSubscriptionDTO toBO(UserServiceSubscription uss) {
        UserServiceSubscriptionDTO bo = new UserServiceSubscriptionDTO();
        BeanUtils.copyProperties(uss, bo);
        bo.setUserId(uss.getUser().getId());
        bo.setUserPhone(uss.getUser().getPhone());
        return bo;
    }

    @Override
    public UserServiceSubscriptionDTO addUserServiceSubscription(UserServiceSubscriptionAddParam param) {
        UserServiceSubscription uss = userServiceSubscriptionRepository.findByUserIdAndServiceAndStatusIsNot(param.getUserId(), param.getService(), UserServiceSubscriptionStatus.DELETED);
        if (uss == null) {
            uss = new UserServiceSubscription();
            uss.setUser(userRepository.findOne(param.getUserId()));
            uss.setService(param.getService());
            uss.setCreateTime(new Date());
            uss.setExpireTime(DateUtils.addMonths(new Date(), param.getTimeValue() * param.getTimeUnit().month));
            uss.setStatus(UserServiceSubscriptionStatus.VALID);
        } else {
            uss.setExpireTime(DateUtils.addMonths(uss.getExpireTime(), param.getTimeValue() * param.getTimeUnit().month));
            uss.setUpdateTime(new Date());
        }
        this.userServiceSubscriptionRepository.save(uss);
        return toBO(uss);
    }

    @Override
    public void setStatus(long id, UserServiceSubscriptionStatus status) {
        UserServiceSubscription uss = userServiceSubscriptionRepository.findOne(id);
        userServiceSubscriptionService.setStatus(uss.getUser().getId(), uss.getService(), status);
    }

    @Override
    public void setStatus(long userId, Service service, UserServiceSubscriptionStatus status) {
        List<UserServiceSubscription> ussList = userServiceSubscriptionRepository.findByUserIdAndService(userId, service);
        for (UserServiceSubscription userServiceSubscription : ussList) {
            userServiceSubscription.setStatus(status);
        }
        userServiceSubscriptionRepository.save(ussList);
    }

    @Override
    public boolean checkServiceValid(long userId, Service service) {
        return userServiceSubscriptionRepository.findByUserIdAndServiceAndStatus(userId, service, UserServiceSubscriptionStatus.VALID) != null;
    }
}
